home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994 February: Tool Chest / Dev.CD Feb 94.toast / Tool Chest / Development Platforms / AppsToGo / AppsToGo.src / =AppWannabe.Tutorial < prev    next >
Encoding:
Text File  |  1993-06-18  |  23.1 KB  |  591 lines  |  [TEXT/MPS ]

  1. AppWannabe QuickStart:
  2.  
  3. Step 1 is to build the libraries.  If you have not built the libraries, please see either the
  4. “==MPW read me” or “==THINK read me” file.
  5.  
  6. Getting started with the tutorial:
  7.  
  8. Step 1)  Copy the AppWannabe project folder.
  9. Step 2)  If you are using MPW, you may wish to delete the files “AppWannabe.π” and
  10.          “AppWannabe.π.rsrc”.  If you are using THINK, you may wish to delete the
  11.          file “AppWannabe.make”.
  12. Step 3)  You may wish to change the name of the project from AppWannabe.  Change the file name
  13.          for those files names AppWannabe-something.  This tutorial will assume that you have
  14.          left the project names AppWannabe.
  15. Step 4)  Build AppWannabe.  If you changed the project name, and you are using MPW, you will have
  16.          to change the “AppName” in the make file.
  17. Step 5)  Run AppWannabe.  Note that the “File” and “Edit” menus are already implemented.
  18.          (The rest of the menus are your problem.)
  19.  
  20. So, you now have the standard AppWannabe running.  Let's change it.
  21.  
  22. The most noticeable change you can make is to get something to draw in the window.  The code
  23. for drawing in the window is in the file “Window.c”.
  24.  
  25. So, let's open the source file “Window.c” and take a look.  A quick way to see what this
  26. source file is about is to search for •• marks.  The functions that have •• in the comments
  27. are automatically called by the library's framework code.  All you need to do is to drop
  28. some code into these functions, and the framework calls it at the right times.
  29.  
  30. Sounds good.  Does it work?  Let's try it.
  31.  
  32. Find the function “ImageDocument” and add the following code to it:
  33.  
  34.     Rect        rct;
  35.  
  36.     SetRect(&rct, 10, 10, 300, 200);
  37.     FrameRect(&rct);
  38.  
  39. The code that's already there draws any controls that may have been added to the AppWannabe
  40. application using AppsToGo.  You'll want to leave that code there.
  41.  
  42. ImageDocument() should now look like this:
  43.  
  44. OSErr    ImageDocument(FileRecHndl frHndl)
  45. {
  46. #pragma unused (frHndl)
  47.  
  48.     WindowPtr    curPort;
  49.     Rect        rct;
  50.  
  51.     SetRect(&rct, 10, 10, 300, 200);
  52.     FrameRect(&rct);
  53.  
  54.     GetPort(&curPort);
  55.     if (!gPrintPage) {                            /* If not printing... */
  56.         DoDrawControls(curPort, false);            /* Draw the content controls. */
  57.     }
  58.     gPrintPage = 0;
  59.  
  60.     return(noErr);
  61. }
  62.  
  63. Build it and run it.  A simple rect shows up in the window.  We can note some interesting
  64. behaviors at this point.  Note that the zoom box zooms to the size that the window already
  65. is.  That is because the window is zoomed to the document size.  The default document size
  66. is the size of the window based on the 'WIND' resource for the window (number 128).
  67.  
  68. We can change the initial window size in two ways:
  69.  
  70. 1) We can change the resource 'WIND', ID#128.
  71. 2) We can change the code to override the default document size.
  72.  
  73. Method 1 doesn't really change anything.  The window will open a different size, but
  74. the zoom box will still zoom to the same (new) size, as the default document size is
  75. still the 'WIND' resource size.  So let's do method 2.
  76.  
  77. At the point in the document where ImageDocument is called, it is too late to change the
  78. size of the window.  You could, but the window is already being displayed, so changing it
  79. here would be ugly.  However, we could change the document size at this point.  Even though
  80. ImageDocument() isn't the place to do this, let's change it there anyway.
  81.  
  82. There is a library call to change the document size.  It records the new size, plus it
  83. adjusts the scrollbar max values to reflect that more area can now be scrolled to.
  84. Here's the prototype for the call:
  85.  
  86. void    SetDocSize(FileRecHndl frHndl, long hSize, long vSize);
  87.  
  88. Just pass in the file record handle (frHndl), and the new horizontal and vertical size
  89. of the document.
  90.  
  91. ImageDocument() is passed in the frHndl of the document to be imaged, so all we have to do
  92. is to pass it to SetDocSize().
  93.  
  94. NOTE:  For MPW users, passing frHndl to SetDocSize() means that frHndl is no longer not used.
  95.        You probably want to remove the #pragma unused when you add this code.
  96.  
  97.  
  98. So we would add the following line to ImageDocument():
  99.  
  100.     SetDocSize(frHndl, 2000, 2000);                /* Make document plenty big. */
  101.  
  102.  
  103. ImageDocument() would now look like this:
  104.  
  105. OSErr    ImageDocument(FileRecHndl frHndl)
  106. {
  107.     WindowPtr    curPort;
  108.     Rect        rct;
  109.  
  110.     SetDocSize(frHndl, 2000, 2000);                /* Make document plenty big. */
  111.  
  112.     SetRect(&rct, 10, 10, 300, 200);
  113.     FrameRect(&rct);
  114.  
  115.     GetPort(&curPort);
  116.     if (!gPrintPage) {                            /* If not printing... */
  117.         DoDrawControls(curPort, false);            /* Draw the content controls. */
  118.     }
  119.     gPrintPage = 0;
  120.  
  121.     return(noErr);
  122. }
  123.  
  124.  
  125. Now the zoom actually changes the size of the window.  However, it becomes apparent that
  126. there is something wrong when the window is zoomed, compared to how the window initially
  127. comes up.  When the window initially comes up, the scrollbars are still inactive, but
  128. when the window is zoomed, they become active.  They should be active all of the time, since
  129. we set the document size substantially greater than the viewable part of the window.
  130.  
  131. So, what's the deal?
  132.  
  133. The deal is that the scrollbars are active when the window initially comes up.  They just
  134. don't look it.  Try creating a new window and then start scrolling right away.  The
  135. scrollbars do actually work.  Well, this is a bug, isn't it?
  136.  
  137. Yep.  It's a bug.  The bug is that SetDocSize() didn't redisplay the scrollbars when we
  138. called it.  Actually SetDocSize is fine.  The problem is that the scrollbars are clipped
  139. out of the drawing area when we are in ImageDocument().  This is actually good, as we
  140. don't want the content of the window drawing over our scrollbars.  The library first
  141. removes the frame area from the drawable portion of a window, and then the library calls
  142. ImageDocument().
  143.  
  144. What this means is that the SetDocSize() call doesn't belong in the ImageDocument() function.
  145. (I said it wasn't a good place for it.)  Well, where does it belong?  You could place the
  146. call in the function InitContent().  InitContent() is called after the window is created,
  147. but before it is displayed, and you now want to initialize the content of the window.
  148. Moving the SetDocSize() call to here will make this work quite nicely.  Try it.  Put the
  149. ImageDocument() function back to the way it was prior to our adding the SetDocSize call to
  150. it, and place the SetDocSize() call in the “Window.c” function InitContent().  (MPW users
  151. have to contend with “#pragma unused” statements.)  The InitContent() function would look
  152. like this:
  153.  
  154. OSErr    InitContent(FileRecHndl frHndl, WindowPtr window)
  155. {
  156.     OSErr    err;
  157.  
  158.     SetDocSize(frHndl, 2000, 2000);
  159.  
  160.     err = AddControlSet(window, (*frHndl)->fileState.sfType, kwStandardVis, 0, 0, nil);
  161.     return(err);
  162. }
  163.  
  164. The AddControlSet() call is so that controls added in AppsToGo will be added to the
  165. content of the window, so calling AddControlSet() here is correct.
  166.  
  167. Now the scrollbars appear initially correct.  Note that the initial window size hasn't
  168. changed.  As a point of clarification, I should state that InitContent() is called only
  169. once per window.  It is only called when a window is getting created.  ImageDocument()
  170. gets called every time the window needs redrawing (or when the user is printing, but
  171. more on this later).
  172.  
  173. What if we wanted the window to initially open to reflect the size of the document?  Of
  174. course a document as large as 2000,2000 isn't going to fit on many monitors, but how about
  175. trying to open the document this big, not to exceed the size of the monitor?  What this
  176. means is setting the document size from within the InitContent() function is too late.
  177. The window has already been created and sized, and InitContent() is being called to make
  178. changes to the content of a window, not the window itself.  We could also resize the window,
  179. as it hasn't been displayed yet, but that's not the best thing to do.  The window creation
  180. function in DTS.framework went to a lot of trouble to position the window base on its size.
  181. Changing the size now would cause us more work than we want at this point.  (DTS.framework
  182. staggers and auto-sizes windows, plus it leaves a pleasant border around the window.  We
  183. want to utilize all of this behavior.)
  184.  
  185. So, if InitContent() is too late, where isn't it too late?
  186.  
  187. Here's how you work with documents in this framework.  You first try to create a document,
  188. (an frHndl), and if you succeed, you then give the document a window.  You can do stuff
  189. between creating a document and giving the document a window.  The user doesn't see the
  190. actual document.  They just see the window you give the document, so until you give the
  191. document a window, the user will see nothing.  This is where you would want to set the
  192. document size, between document creation and window creation.
  193.  
  194. Since you want to set the document size prior to having a window, it is reasonable to
  195. expect that you will have to add code to some file other than the “Window.c” file.
  196. Again, this is the case.  You want to add code to the file “File.c”.  The function that
  197. you want to add code to is called InitDocument.
  198.  
  199. InitDocument() looks like this:
  200.  
  201.  
  202. OSErr    InitDocument(FileRecHndl frHndl)
  203. {
  204.     OSErr    err;
  205.  
  206.     err = noErr;
  207.  
  208.     switch ((*frHndl)->fileState.sfType) {
  209.         case kDocFileType:
  210.             err = DefaultInitDocument(frHndl, kVersion, kMaxNumUndos, kNumSaveUndos);
  211.             if (!err) {
  212.                 /* Any additional document initialization could go here. */
  213.             }
  214.             break;
  215. #if VH_VERSION
  216.         case kViewHierFileType:
  217.             return(VHInitDocument(frHndl));
  218.             break;
  219. #endif
  220.         default:
  221.             err = DefaultInitDocument(frHndl, kVersion, kMaxNumUndos, kNumSaveUndos);
  222.             if (!err) {
  223.                 (*frHndl)->fileState.readDocumentProc  = nil;
  224.                 (*frHndl)->fileState.writeDocumentProc = nil;
  225.             }
  226.             break;
  227.     }
  228.  
  229.     return(err);
  230. }
  231.  
  232.  
  233. InitDocument() is given a file reference (frHndl).  The frHndl contains a bunch of file
  234. information, one part of which is the file type.  The switch statement gets this type
  235. from the frHndl and then the case statements do the appropriate thing based on that type.
  236.  
  237. You don't have to worry about the kViewHierFileType case statement.  That is there so that
  238. the View Hierarchy debugging facility is available.  Discarding this file type, there is
  239. only one file type left, kDocFileType.  If you have only one type of document in your
  240. application, this is all you will ever need.  If you add document types to your application,
  241. you will end up with more case statements here.
  242.  
  243. You want to replace the comment “Any additional document...” with your code, so that the
  244. function looks like this:
  245.  
  246. OSErr    InitDocument(FileRecHndl frHndl)
  247. {
  248.     OSErr    err;
  249.  
  250.     err = noErr;
  251.  
  252.     switch ((*frHndl)->fileState.sfType) {
  253.         case kDocFileType:
  254.             err = DefaultInitDocument(frHndl, kVersion, kMaxNumUndos, kNumSaveUndos);
  255.             if (!err) {
  256.                 SetDocSize(frHndl, 2000, 2000);
  257.             }
  258.             break;
  259. #if VH_VERSION
  260.         case kViewHierFileType:
  261.             return(VHInitDocument(frHndl));
  262.             break;
  263. #endif
  264.         default:
  265.             err = DefaultInitDocument(frHndl, kVersion, kMaxNumUndos, kNumSaveUndos);
  266.             if (!err) {
  267.                 (*frHndl)->fileState.readDocumentProc  = nil;
  268.                 (*frHndl)->fileState.writeDocumentProc = nil;
  269.             }
  270.             break;
  271.     }
  272.  
  273.     return(err);
  274. }
  275.  
  276.  
  277. Don't forget to yank the SetDocSize() call out of InitContent(), as we have taken care of
  278. setting the document size elsewhere and don't need it anymore.  Run it and test it out.
  279.  
  280.  
  281. So that was reasonably painless.  What else can we easily do?
  282.  
  283. How about adding a ruler to our window?
  284.  
  285. Since we are in the function InitDocument(), let's set another document attribute.  Let's set
  286. the topSidebar to a non-zero value and see what happens.  Add the following lines below the
  287. SetDocSize() call:
  288.  
  289.                 SetSidebarSize(frHndl, kwNoChange, 40);
  290.                     /* Don't change the left sidebar (leave it 0),
  291.                     ** but set the top sidebar to 40. */
  292.  
  293. Now when you run the application, you see that the vertical scrollbar doesn't go all the
  294. way to the top anymore.  There's a 40 pixel gap at the top of the window.  Try scrolling
  295. the rect around the window.  Note that it is clipped out of this area.  This area shouldn't
  296. scroll with with document, as we want to use it as a ruler.  All we need to do now is to
  297. write some code to draw the ruler.
  298.  
  299. So, where does our ruler drawing code go?  It goes in a function called DrawFrame().
  300. DrawFrame() is found in the “Window.c” file.  It is also called automatically by the
  301. framework when the frame needs redrawing.  I consider it part of the frame, just like
  302. the scrollbars and grow icon.  The frame portion of the window can't be drawn over by
  303. the ImageDocument procedure. This is why the rect gets scrolled behind this top area.
  304. The framework sets the origin to -16384,0 prior to calling DrawFrame.  The frame
  305. origin is offset by -16384 horizontally so that controls in the frame area can be
  306. drawn simply by calling DoDrawControls().  Controls with an origin offset -16384
  307. horizontally will be drawn, and all other controls will be pushed out of the window
  308. and therefore will not display in the frame.
  309.  
  310. Given that the origin is -16384,0, here's some quick-and-ugly ruler code that we can use:
  311.  
  312.     short    i;
  313.  
  314.     for (i = 0; i < 16; ++i) {
  315.         MoveTo(72 * i - 16384, 0);
  316.         Line(0, 16);                    /* Draw inch marks. */
  317.  
  318.         MoveTo(72 * i + 36 - 16384, 0);        /* Draw 1/2 inch marks. */
  319.         Line(0, 8);
  320.  
  321.         MoveTo(72 * i + 18 - 16384, 0);        /* Draw 1/4 inch marks. */
  322.         Line(0, 4);
  323.         MoveTo(72 * i + 54 - 16384, 0);
  324.         Line(0, 4);
  325.     }
  326.  
  327. Add it to the DrawFrame() function “Window.c”.  The code that's already there draws lines
  328. so the edge of the frame can be seen.  Also, the code:
  329.  
  330.     BeginFrame(window);
  331.     DoDrawControls(window, activate);
  332.     EndFrame(window);
  333.  
  334. draws any controls that were added to the frame using AppsToGo, so leave that there, too.
  335.  
  336.  
  337. After adding the above code to DrawFrame(), it looks like this:
  338.  
  339. void    DrawFrame(FileRecHndl frHndl, WindowPtr window, Boolean activate)
  340. {
  341.     short    i;
  342.  
  343.     for (i = 0; i < 16; ++i) {
  344.         MoveTo(72 * i - 16384, 0);
  345.         Line(0, 16);                    /* Draw inch marks. */
  346.  
  347.         MoveTo(72 * i + 36 - 16384, 0);        /* Draw 1/2 inch marks. */
  348.         Line(0, 8);
  349.  
  350.         MoveTo(72 * i + 18 - 16384, 0);        /* Draw 1/4 inch marks. */
  351.         Line(0, 4);
  352.         MoveTo(72 * i + 54 - 16384, 0);
  353.         Line(0, 4);
  354.     }
  355.  
  356.     MoveTo(0, (*frHndl)->fileState.topSidebar - 1);
  357.     LineTo((*frHndl)->fileState.leftSidebar - 1 - 16384, (*frHndl)->fileState.topSidebar - 1);
  358.     LineTo((*frHndl)->fileState.leftSidebar - 1 - 16384, 16383);
  359.  
  360.     BeginFrame(window);
  361.     DoDrawControls(window, activate);
  362.     EndFrame(window);
  363. }
  364.  
  365.  
  366. We could add text to show the inches and stuff, but this is just a tutorial, so the
  367. heck with it.
  368.  
  369. Now, let's run the application and note a problem.  If we scroll vertically, everything works
  370. just fine, but if we scroll horizontally, we should really scroll the ruler, too.
  371.  
  372. No problem.  The framework calls us at the right time.  The function ScrollFrame() is called
  373. when the document is scrolled.  Often you will do nothing, as you may not even have rulers
  374. in your document, but here we do care.
  375.  
  376. There are a couple of ways we could address this.  One is to use ScrollRect() to scroll the
  377. ruler the right amount, and then call DrawFrame().  Here's some code to do this:
  378.  
  379. void    ScrollFrame(FileRecHndl frHndl, WindowPtr window, long dh, long dv)
  380. {
  381.     WindowPtr    oldPort;    
  382.     Rect        rct;
  383.     RgnHandle    rgn;
  384.  
  385.     GetPort(&oldPort);
  386.     SetPort(window);
  387.     SetOrigin(0, 0);
  388.  
  389.     rct = window->portRect;        /* Get the window's portRect, so we can scroll the top (ruler). */
  390.     rct.bottom = 39;            /* Not 40, as there is no need to scroll the bottom line. */
  391.     rgn = NewRgn();
  392.     ScrollRect(&rct, dh, 0, rgn);        /* Only scroll horizontally. */
  393.     DisposeRgn(rgn);
  394.     DrawFrame(frHndl, window, ((WindowPeek)window)->hilited);
  395.  
  396.     SetPort(oldPort);
  397. }
  398.  
  399. Try it.  It works pretty crummy, huh?
  400.  
  401. The only problem is that the code we wrote for DrawFrame() assumed an origin of -16384,0.
  402. Since the ruler can now scroll, we have to take this into account.  Here's the new DrawFrame()
  403. code that takes the horizontal origin into account:
  404.  
  405. void    DrawFrame(FileRecHndl frHndl, WindowPtr window, Boolean activate)
  406. {
  407.     short    i;
  408.     Point    org;
  409.  
  410.     GetContentOrigin(window, &org);
  411.     SetOrigin(org.h, 0);
  412.  
  413.     for (i = 0; i < 16; ++i) {
  414.         MoveTo(72 * i, 0);
  415.         Line(0, 16);                /* Draw inch marks. */
  416.  
  417.         MoveTo(72 * i + 36, 0);        /* Draw 1/2 inch marks. */
  418.         Line(0, 8);
  419.  
  420.         MoveTo(72 * i + 18, 0);        /* Draw 1/4 inch marks. */
  421.         Line(0, 4);
  422.         MoveTo(72 * i + 54, 0);
  423.         Line(0, 4);
  424.     }
  425.  
  426.     SetOrigin(0, 0);
  427.     MoveTo(16383, (*frHndl)->fileState.topSidebar - 1);
  428.     LineTo((*frHndl)->fileState.leftSidebar - 1, (*frHndl)->fileState.topSidebar - 1);
  429.     LineTo((*frHndl)->fileState.leftSidebar - 1, 0);
  430.  
  431.     BeginFrame(window);                    /* BeginFrame resets the origin to -16384,0. */
  432.     DoDrawControls(window, activate);
  433.     EndFrame(window);
  434. }
  435.  
  436.  
  437. So that covers how to scroll the ruler using the toolbox call ScrollRect.  Here's a way to
  438. scroll the ruler without calling ScrollRect.  We first change ScrollFrame to simply
  439. call DrawFrame, and then we let DrawFrame do all the work.
  440.  
  441. We can use the offscreen drawing package GWLayers to first draw the ruler offscreen, and
  442. then transfer it to the window.  Here's the DrawFrame code to do this:
  443.  
  444.  
  445. void    DrawFrame(FileRecHndl frHndl, WindowPtr window, Boolean activate)
  446. {
  447.     WindowPtr    oldPort;
  448.     Point        oldOrg, contOrg;
  449.     LayerObj    windowLayer, rulerLayer;
  450.     Rect        rct;
  451.     short        i;
  452.  
  453.     GetPort(&oldPort);                    /* The framework sets the port for us, and it sets     */
  454.     SetPort(window);                    /* the origin to -16384,0, but now ScrollFrame can     */
  455.     oldOrg.h = window->portRect.left;    /* call here, so do the port saving and origin stuff,  */
  456.     oldOrg.v = window->portRect.top;    /* since we don't know where we were called from.  The */
  457.                                         /* upper-left of the window's portRect is the origin,  */
  458.                                         /* so by saving those, we will be able to set the      */
  459.                                         /* origin back to whatever it was before.              */
  460.  
  461.     GetContentOrigin(window, &contOrg);        /* Find out where the document is scrolled.    */
  462.     SetOrigin(contOrg.h, 0);                /* Set the window's horizontal origin to that. */
  463.  
  464.     NewLayer(&windowLayer, nil, nil, window, 0, 0L);    /* We are using GWLayers to draw   */
  465.     rct        = window->portRect;                        /* offscreen.  The NewLayer call   */
  466.     rct.bottom = 39;                                    /* created a layer object for the  */
  467.     (*windowLayer)->dstRect = rct;                        /* whole window.  We only want the */
  468.                                                         /* top 39 pixels, so set dstRect   */
  469.                                                         /* to the top part of the window.  */
  470.  
  471.     NewLayer(&rulerLayer, windowLayer, nil, nil, 0, 0L);
  472.     SetLayerWorld(rulerLayer);
  473.         /* We may not have enough memory to create the offscreen GWorld, so this call
  474.         ** may fail, but that's actually okay.  If NewLayer fails, it returns nil for
  475.         ** rulerLayer.  If we pass nil into SetLayerWorld, it does nothing.  If it does
  476.         ** nothing, then the port is still set to the last port, which is the document's
  477.         ** window (SetPort above).  This means that in the worst case, the ruler will
  478.         ** flicker when it is redrawn.  We will not blow up. */
  479.  
  480.     EraseRect(&rct);
  481.         /* Erase the offscreen GWorld (or possibly the ruler in the window if low on ram). */
  482.  
  483.     for (i = 0; i < 16; ++i) {
  484.         MoveTo(72 * i, 0);
  485.         Line(0, 16);                /* Draw inch marks. */
  486.  
  487.         MoveTo(72 * i + 36, 0);        /* Draw 1/2 inch marks. */
  488.         Line(0, 8);
  489.  
  490.         MoveTo(72 * i + 18, 0);        /* Draw 1/4 inch marks. */
  491.         Line(0, 4);
  492.         MoveTo(72 * i + 54, 0);
  493.         Line(0, 4);
  494.     }
  495.  
  496.     InvalLayer(windowLayer, GetEffectiveDstRect(windowLayer), false);
  497.         /* Invalidate the entire layer so it all transfers to the window. */
  498.  
  499.     UpdateLayer(windowLayer);                /* Transfer all invalid areas. */
  500.     ResetLayerWorld(rulerLayer);            /* Undo the above SetLayerWorld. */
  501.     DisposeThisAndBelowLayers(windowLayer);    /* Dispose of all the layers we created */
  502.                                             /* in this function. */
  503.  
  504.     SetOrigin(0, 0);
  505.     MoveTo(16383, (*frHndl)->fileState.topSidebar - 1);
  506.     LineTo((*frHndl)->fileState.leftSidebar - 1, (*frHndl)->fileState.topSidebar - 1);
  507.     LineTo((*frHndl)->fileState.leftSidebar - 1, 16383);
  508.  
  509.     BeginFrame(window);                    /* BeginFrame resets the origin to -16384,0. */
  510.     DoDrawControls(window, activate);
  511.     EndFrame(window);
  512.  
  513.     SetOrigin(oldOrg.h, oldOrg.v);        /* Put origin and port back the way they were. */
  514.     SetPort(oldPort);
  515. }
  516.  
  517.  
  518. And ScrollFrame() looks like this:
  519.  
  520. void    ScrollFrame(FileRecHndl frHndl, WindowPtr window, long dh, long dv)
  521. {
  522. #pragma unused (dh, dv)
  523.  
  524.     DrawFrame(frHndl, window, ((WindowPeek)window)->hilited);
  525. }
  526.  
  527.  
  528. The first method seems simpler, but for certain types of rulers, you may wish to use this
  529. technique.  The first method first does a ScrollRect(), and then it redraws.  The
  530. ScrollRect() erases the area that scrolled into the window.  It then gets redrawn via
  531. calling DrawFrame(). This means that for an instant part of the ruler is white.  If the
  532. drawing of the ruler is fast, then this is no big deal.  If however, your ruler is
  533. somewhat complicated and takes a while to draw, then you might want to use this second
  534. technique.
  535.  
  536. This second method was also a good way to introduce you to the GWLayers code.  Note that
  537. GWLayers also works for system 6, even if the GWorld calls aren't available.  There is
  538. no system 6 v.s. system 7 compatibility hit if you use GWLayers.
  539.  
  540.  
  541. Another nice feature of the second sample is that DrawFrame() is robust.  It doesn't depend
  542. on the framework setting up things nicely (setting the port, setting the origin).
  543. It handles these details, which allows you to call it from less polite code, such as the
  544. ScrollFrame() procedure for this method.
  545.  
  546. So, DrawFrame() is very robust, and ScrollFrame() is really stupid.  What could be better?
  547.  
  548. The above code isn't very object-oriented.  If your application has only one document type,
  549. then there's nothing wrong with the above code.  However, if you have more than one document
  550. type, then you should code ScrollFrame() so that it uses whatever DrawFrame() procedure the
  551. document has set.
  552.  
  553. You probably have been wondering how the framework knows what function to call.  In effect,
  554. it doesn't.  A pointer to the appropriate function is stored in the frHndl for each of these
  555. functions.  You can change any of these function pointers.  Let's say that you create a
  556. second document type that has a different ruler.  Possibly the other document type has a
  557. vertical ruler, instead of the horizontal ruler we use in this example.  This second
  558. document would have a different pointer for its DrawFrame() function.  However, it could
  559. have the same pointer for the ScrollFrame() function.  Given this possibility, here's what
  560. should be done in ScrollFrame():
  561.  
  562.  
  563.  
  564. void    ScrollFrame(FileRecHndl frHndl, WindowPtr window, long dh, long dv)
  565. {
  566. #pragma unused (dh, dv)
  567.  
  568.     DrawFrameProcPtr    proc;
  569.  
  570.     if (proc = (*frHndl)->fileState.drawFrameProc)
  571.         (*proc)(frHndl, window, ((WindowPeek)window)->hilited);
  572. }
  573.  
  574.  
  575. Unless the DrawFrame() procedure pointer is changed, the above code does exactly the same
  576. thing.  When a document is created, the application framework sets the procedure pointers
  577. to initial values.  The initial values are the functions found in the “Window.c” file.  If
  578. you have only one document type, then you can leave these pointers just as they are and
  579. simply drop code into the appropriate functions in “Window.c”, just as we have been doing
  580. so far.  However, if you have multiple document types in your application, you are most
  581. likely going to want to change some of these procedure pointers.
  582.  
  583. With this flexibility comes the responsibility of not making direct calls.  We need to look
  584. up the procedure pointer that the document wants used, and then call that procedure.
  585. An additional feature of the framework is that if the procedure pointer is nil, then that
  586. particular facility isn't used.  This allows you to turn off any of the features of a
  587. document simply by setting the procedure pointer to nil.  So, before you call one of these
  588. procedures, you first get the procedure pointer, and then if it isn't nil, you call it.
  589.  
  590.  
  591.